package com.sdy.dataadapter;

import com.sdy.common.utils.StringUtil;
import lombok.Data;

import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicBoolean;

@Data
public class RawDataSource {
    private DbType dbType;
    private DbDriver dbDriver;
    private String url;
    private String username;
    private String password;
    private static final Map<String, String> MYSQL_CJ_PARAM_MAP = new HashMap<>();
    private static final Map<String, String> SQL_SERVER_PARAM_MAP = new HashMap<>();
    
    static {
        MYSQL_CJ_PARAM_MAP.put("serverTimezone", "Asia/Shanghai");
        MYSQL_CJ_PARAM_MAP.put("useSSL", "false");
        MYSQL_CJ_PARAM_MAP.put("useUnicode", "true");
        MYSQL_CJ_PARAM_MAP.put("characterEncoding", "UTF-8");

        SQL_SERVER_PARAM_MAP.put("encrypt", "false");
        SQL_SERVER_PARAM_MAP.put("trustServerCertificate", "false");
        SQL_SERVER_PARAM_MAP.put("loginTimeout", "30");
    }

    public RawDataSource(DbType dbType, DbDriver dbDriver, String url, String username, String password) {
        this.dbType = dbType;
        this.dbDriver = dbDriver;
        this.url = url;
        this.username = username;
        this.password = password;
    }

    public RawDataSource(DbType dbType, String ip, Integer port, String dbname, String username, String password, Map<String, String> paramMap) {
        this.dbType = dbType;
        if (dbType == null
                || StringUtil.isBlank(ip)
                || port == null
                || StringUtil.isBlank(dbname)
                || StringUtil.isBlank(username)
                || StringUtil.isBlank(password)) {
            throw new RuntimeException("Parameter error!");
        }
        if (dbType.equals(DbType.MYSQL)) {
            this.dbDriver = DbDriver.MYSQL_CJ;
            this.url = String.format("jdbc:mysql://%s:%d/%s%s", ip, port, dbname, formatParam(MYSQL_CJ_PARAM_MAP, paramMap));
        } else if (dbType.equals(DbType.ORACLE)) {
            this.dbDriver = DbDriver.ORACLE;
            this.url = String.format("jdbc:oracle:thin:@%s:%d:%s", ip, port, dbname);
        } else if (dbType.equals(DbType.SQL_SERVER)) {
            this.dbDriver = DbDriver.SQL_SERVER;
            if (paramMap == null) {
                paramMap = new HashMap<>();
            }
            paramMap.put("database", dbname);
            paramMap.put("user", username);
            paramMap.put("password", password);
            this.url = String.format("jdbc:sqlserver://%s:%d;%s", ip, port, formatParamSqlServer(SQL_SERVER_PARAM_MAP, paramMap));
        } else if (dbType.equals(DbType.DB2)) {
            this.dbDriver = DbDriver.DB2;
            this.url = String.format("jdbc:oracle:thin:@%s:%d:%s", ip, port, dbname);
        } else {
            throw new RuntimeException("Unimplemented dbType!");
        }
        this.username = username;
        this.password = password;
    }
    
    private String formatParam(Map<String, String> defaultMap, Map<String, String> paramMap) {
        Map<String, String> map = new HashMap<>(8);
        if (defaultMap != null) {
            map.putAll(defaultMap);
        }
        if (paramMap != null) {
            map.putAll(paramMap);
        }
        if (map.isEmpty()) {
            return "";
        }
        StringBuilder paramStr = new StringBuilder("?");
        AtomicBoolean first = new AtomicBoolean(true);
        map.forEach((k, v) -> {
            if (first.get()) {
                first.set(false);
            } else {
                paramStr.append("&");
            }
            paramStr.append(k).append("=").append(v);
        });
        return paramStr.toString();
    }

    private String formatParamSqlServer(Map<String, String> defaultMap, Map<String, String> paramMap) {
        Map<String, String> map = new HashMap<>(8);
        if (defaultMap != null) {
            map.putAll(defaultMap);
        }
        if (paramMap != null) {
            map.putAll(paramMap);
        }
        if (map.isEmpty()) {
            return "";
        }
        StringBuilder paramStr = new StringBuilder();
        map.forEach((k, v) -> paramStr.append(k).append("=").append(v).append(";"));
        return paramStr.toString();
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (o == null || getClass() != o.getClass()) {
            return false;
        }
        RawDataSource that = (RawDataSource) o;
        return dbType.getDb().equals(that.dbType.getDb()) &&
                Objects.equals(url, that.url) &&
                Objects.equals(username, that.username);
    }

    @Override
    public int hashCode() {
        return Objects.hash(dbType.getDb(), url, username);
    }
}
